/*****************************************************************************
Copyright 2006 Kervin L. Pierre
    EMAIL: otlkcon@adevsoft.com

This file is part of Otlkcon.

Otlkcon is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

Otlkcon is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Lesser GNU General Public License for more details.

You should have received a copy of the Lesser GNU General Public License
along with Otlkcon; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*******************************************************************************/

#include "StdAfx.h"

#ifdef OTLKCON_XP

#include "O_IXPProvider.h"
#include "mstore.h"

#include <mspst.h>

extern const IID IID_IXPProvider;

//extern vector<otlkcon_generic_msgstore *> otlkcon_stores;
extern CRITICAL_SECTION otlkcon_stores_cs;

extern vector<O_IMSProvider *> otlkcon_store_providers;
extern CRITICAL_SECTION otlkcon_store_providers_cs;

extern map< O_ENTRYID *, O_IContainer *, otlkcon_entryid_less > otlkcon_xpp_wait_set;
extern CRITICAL_SECTION otlkcon_xpp_wait_set_cs;

extern HWND  otlkcon_eventHwnd;

extern map< O_ENTRYID *, void *, otlkcon_entryid_less >      otlkcon_event_objects;
extern CRITICAL_SECTION    otlkcon_objects_cs;

O_IXPProvider::O_IXPProvider()
{
    InitializeCriticalSection(&cs);
    accounts = NULL;
    xpEnabled = FALSE;
}

HRESULT O_IXPProvider::Create(otlkcon_memory *m)
{
    HRESULT result = S_OK;

    memObj = m;
    cRef = 1;

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
        L"O_IXPProvider::Create() called.\n");

    lpXPLogon = NULL;

    // Temporary entryID
    entryID             = (O_ENTRYID*)memObj->malloc( sizeof(O_ENTRYID) );
    result              = O_IProp::NewMAPIUID( &(entryID->instanceGuid) );
    entryID->typeGuid   = otlkcon_xpprovider_guid ;
    //entryID->typeID     = MAPI_PROFSECT; // FIXME: need to find a better way
    entryID->primarySupportedInterface = OTLKCON_IXPPROVIDER_CLASS;

    OTLKCON_ENTERCRITICALSECTION(&otlkcon_objects_cs,
                                "O_IXPProvider::Create");
    otlkcon_event_objects[entryID] = this;
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_objects_cs,
                                "O_IXPProvider::Create");

    accounts = new vector<otlkcon_account_details *>();
    for( int i=0; i<OTLKCON_ACCOUNT_MAX; i++)
    {
        accounts->push_back( new otlkcon_account_details() );
    }

    return result;
}

HRESULT O_IXPProvider::Destroy()
{
    HRESULT result = S_OK;

    return result;
}

STDMETHODIMP O_IXPProvider::QueryInterface (REFIID riid, void **ppv)
{
    HRESULT result = S_OK;

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
        L"inside O_IXPProvider->QueryInterface().\n");

    *ppv = NULL;

    if( memcmp(&riid, &IID_IUnknown, sizeof(IID)) &&
        memcmp(&riid, &IID_IXPProvider, sizeof(IID)))
    { 
        result = ResultFromScode(E_NOINTERFACE);
        return result;
    }

    AddRef();
    *ppv = (LPVOID)this;

    return result;
}

ULONG STDMETHODCALLTYPE O_IXPProvider::AddRef (void)
{
    return ++cRef;
}

ULONG STDMETHODCALLTYPE O_IXPProvider::Release (void)
{
    if ( --cRef == 0 )
    {
        Destroy();
    }

    return cRef;
}

STDMETHODIMP O_IXPProvider::Shutdown(ULONG *lpulFlags)
{
    HRESULT result = S_OK;

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
        L"inside IXPPRovider->Shutdown\n");

    return result;
}

HRESULT O_IXPProvider::RefreshStores()
{
    HRESULT result = S_OK;

    // Get the relevant MSP and ABP objects and stores.

    // FIXME: Just getting the first one for now.
    if( otlkcon_store_providers.size() > 0 )
    {
        lpMSProvider = otlkcon_store_providers[0];
    }
    else
    {
        lpMSProvider = 0;
    }

    // Find the correct "spooler" message store
    O_IMSPMsgStore *tmpMsgStore = NULL;
    for( vector<O_IMSProvider *>::iterator i=otlkcon_store_providers.begin(); 
        i!=otlkcon_store_providers.end(); ++i )
    {
        if( (*i)->IsSpoolerContext() )
        {
            tmpMsgStore = (*i)->RetrieveStore();
        }
    }

    // Otherwise use the first in the list
    if( tmpMsgStore == NULL && lpMSProvider != 0 )
    {
        tmpMsgStore = lpMSProvider->RetrieveStore();
    }

    if( tmpMsgStore )
    {
        MSPMsgStores.push_back( tmpMsgStore );
    }

    return result;
}

HRESULT O_IXPProvider::GetStore(LPMAPISUP lpMAPISup, ULONG ulUIParam,
     O_IXPPMsgStore **msgstore, ULONG ulFlags)
 {
	HRESULT result = S_OK;
	LPPROFSECT lpProfileObj = NULL;
	ULONG profPropsCount = 0, res=0;
	MAPIUID instanceGuid = {0};  
    LPSPropValue profProps;

    // Open the service profile can return the necessary
    // properties if available.

    // Before anything, lets get some information from the profile
    // Get the profile properties
    result = lpMAPISup->OpenProfileSection(NULL, MAPI_MODIFY, &lpProfileObj);
    if( result != S_OK )
    {
        // call failed
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
            L"IXPProvider::GetStore() : OpenProfileSection failed.\n" );
        result = MAPI_E_UNCONFIGURED;
        goto getstore_end;
    }

    SizedSPropTagArray(50, profTags);

    profPropsCount = 0;
    profTags.aulPropTag[profPropsCount++] = PR_SERVICE_UID;
    profTags.cValues = profPropsCount;

    O_ENTRYID *profEID = NULL;
    char      *profProviderDisplay = NULL, *dbFilename = NULL;
    MAPIUID   *profServiceUid = NULL, *profRecordKey = NULL, *profXpProvider = NULL;
    LPPROFSECT lpServiceProfileObj = NULL;
    wchar_t strBuff[MAX_PATH+1] = {0};

    result = lpProfileObj->GetProps((LPSPropTagArray)&profTags, 0, &profPropsCount, &profProps);
    switch( result )
	{
		case MAPI_W_ERRORS_RETURNED:
		case S_OK:			
            for( ULONG i=0; i<profPropsCount; i++ )
            {
                if( PROP_ID(profProps[i].ulPropTag) == PROP_ID(PR_SERVICE_UID) )
				{
					if( PROP_TYPE(profProps[i].ulPropTag) == PT_BINARY )
					{
						profServiceUid = (MAPIUID*)(profProps[i].Value.bin.lpb);
					}
                }
            }
    }

    result = lpMAPISup->OpenProfileSection(profServiceUid, MAPI_MODIFY,
                                &lpServiceProfileObj);
    if(SUCCEEDED(result))
    {
        profPropsCount = 0;
        profTags.aulPropTag[profPropsCount++] = PR_PST_PATH;
        profTags.aulPropTag[profPropsCount++] = PR_OTLKCON_XP_ENABLED;
	    profTags.cValues = profPropsCount;

        result = lpServiceProfileObj->GetProps((LPSPropTagArray)&profTags, 0, &profPropsCount, &profProps);
        switch( result )
        {
            case MAPI_W_ERRORS_RETURNED:
            case S_OK:			
            for( ULONG i=0; i<profPropsCount; i++ )
            {
                if( PROP_ID(profProps[i].ulPropTag) 
                                            == PROP_ID(PR_PST_PATH) )
	            {
		            if( PROP_TYPE(profProps[i].ulPropTag) == PT_STRING8 )
		            {
                        dbFilename = profProps[i].Value.lpszA;
		            }
	            }
                else if( PROP_ID(profProps[i].ulPropTag) 
                                            == PROP_ID(PR_OTLKCON_XP_ENABLED) )
	            {
		            if( PROP_TYPE(profProps[i].ulPropTag) == PT_BOOLEAN )
		            {
                        xpEnabled = profProps[i].Value.b;
		            }
	            }
            }
        }

        result = otlkcon_getAccountsData( accounts, lpServiceProfileObj );
    }

    int dbFilenameValid = 0;
    result = otlkcon_testDbFilename( dbFilename, 
                                &dbFilenameValid );

    if( dbFilenameValid == 0 )
    {
        // db file not found
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
            L"IMAPIProp database file not found.\n" );
        result = MAPI_E_UNCONFIGURED;
        goto getstore_end;
    }

    // If any of the following properties exist, we should use those
    // instead of generating new values??  If they do not exist, then
    // generate new values??

    profPropsCount = 0;
    profTags.aulPropTag[profPropsCount++] = PR_OTLKCON_ENTRYID;
    profTags.aulPropTag[profPropsCount++] = PR_PROVIDER_DISPLAY;
    profTags.aulPropTag[profPropsCount++] = PR_RECORD_KEY;
    profTags.aulPropTag[profPropsCount++] = PR_SERVICE_UID;
    profTags.aulPropTag[profPropsCount++] = PR_PROVIDER_UID;
    profTags.aulPropTag[profPropsCount++] = PR_PST_PATH;
	profTags.cValues = profPropsCount;

    result = lpProfileObj->GetProps((LPSPropTagArray)&profTags, 0, &profPropsCount, &profProps);
    switch( result )
	{
		case MAPI_W_ERRORS_RETURNED:
		case S_OK:			
            for( ULONG i=0; i<profPropsCount; i++ )
            {
                if( PROP_ID(profProps[i].ulPropTag) == PROP_ID(PR_OTLKCON_ENTRYID) )
                {
                    if( PROP_TYPE(profProps[i].ulPropTag) == PT_BINARY )
                    {
                        profEID = (O_ENTRYID*)(profProps[i].Value.bin.lpb);
                    }    
                }
                else if( PROP_ID(profProps[i].ulPropTag) == PROP_ID(PR_PROVIDER_DISPLAY) )
                {
                    if( PROP_TYPE(profProps[i].ulPropTag) == PT_STRING8 )
                    {
                        profProviderDisplay = _strdup(profProps[i].Value.lpszA);
                    }
                }
                else if( PROP_ID(profProps[i].ulPropTag) == PROP_ID(PR_RECORD_KEY) )
                {
                    if( PROP_TYPE(profProps[i].ulPropTag) == PT_BINARY )
                    {
                        profRecordKey = (LPMAPIUID)(profProps[i].Value.bin.lpb);
                    }
                }
                else if( PROP_ID(profProps[i].ulPropTag) == PROP_ID(PR_SERVICE_UID) )
                {
                    if( PROP_TYPE(profProps[i].ulPropTag) == PT_BINARY )
                    {
                        profServiceUid = (MAPIUID*)(profProps[i].Value.bin.lpb);
                    }
                }
            }
			break;

		default:
			//Error condition
            // Call failed 
            OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
                L"GetProfileProperties: GetProps failed.\n" );
            result = MAPI_E_UNCONFIGURED;
            goto getstore_end;
    }

#ifdef OTLKCON_DELETE_MSTORE_DB
    // For debugging, start with a fresh file
    // FIXME: Set a global variable so this is done
    //        only once per load.
    DeleteFile(dbFilename);
    Sleep( OTLKCON_DELETE_PAUSE );
#endif // OTLKCON_DELETE_MSTORE_DB

	memset(&instanceGuid, 0, sizeof(MAPIUID));

    if( profRecordKey )
    {
        //We got a record key from the profile, may as well use it.
        memcpy(&instanceGuid, profRecordKey, sizeof(MAPIUID));
    }

	// create MsgStore object
    (*msgstore) = new O_IXPPMsgStore();
	//Create() needs the support object
	(*msgstore)->lpMAPISup = lpMAPISup;
    (*msgstore)->ulUIParam = ulUIParam;
    (*msgstore)->logonFlags = ulFlags;

	if( profEID )
    {
        // FIXME:  Is this EntryID wrapped?

        // We have already check in memory for this object above        
		result = (*msgstore)->Load(dbFilename, profEID, memObj);
        if( result == MAPI_E_CORRUPT_DATA )
        {
            // TODO:  The database file or store object is corrupt.
            //        Do we need a repair mode or something?
            OTLKCON_DEBUGBREAK;
        }
		else if( result == MAPI_E_NOT_FOUND )
		{
            //We can try to create if the message store did not
            //exist as yet.

			O_ENTRYID* lpOurEntryID	= (O_ENTRYID*)profEID;
			
			result = (*msgstore)->Create(dbFilename, lpOurEntryID->storeInstanceGuid, memObj);
            if( result == MAPI_E_COLLISION )
            {
                // This object exists, but didn't load.
                // Not good....
                OTLKCON_DEBUGBREAK;
            }
		} // if

        if( profRecordKey )
        {
            // FIXME:  We have a record key from the profile
            //         Most likely we will have to change the profile
            //         since all existing objects of this store has
            //         its record key already.
            //OTLKCON_DEBUGBREAK;
        }
    }
    else
    {
        result = (*msgstore)->Create(dbFilename, instanceGuid, memObj);
        if( result != S_OK )
        {
            OTLKCON_DEBUGBREAK
            goto getstore_end;
        }
    }

	// Set any profiles properties needed.
	SPropValue tempProps[8] = {0};
    //update profile if we need that
	if(NULL == profEID)
    {

		//FIXME: This may cause conflict between multiple msgstore instances
		tempProps[0].ulPropTag = PR_RECORD_KEY;
		tempProps[0].Value.bin.lpb 
            = (LPBYTE)(profRecordKey?profRecordKey:&((*msgstore)->entryID->storeInstanceGuid));
        tempProps[0].Value.bin.cb  = sizeof(MAPIUID);

		tempProps[1].ulPropTag = PR_PST_PATH;
		tempProps[1].Value.lpszA = dbFilename;

		//We need to set entryID
		tempProps[2].ulPropTag = PR_OTLKCON_ENTRYID;
        tempProps[2].Value.bin.lpb = (LPBYTE)((*msgstore)->entryID);
        tempProps[2].Value.bin.cb  = sizeof(O_ENTRYID);

		result = lpProfileObj->SetProps(3, tempProps, NULL);
		
    } // if
	if(NULL != lpProfileObj)
	{
		lpProfileObj->Release();
		lpProfileObj = NULL;
	} // if
    
	if( FAILED(result) )
    {
        OTLKCON_DEBUGBREAK
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
            L"ERROR: result == %x", result );
        
        // TODO: cleanup, build error struct 
        result = MAPI_E_FAILONEPROVIDER;
        goto getstore_end;
    }

  //  if(profRecordKey)
  //  {
		//lpMAPISup->ModifyProfile(ulFlags & MDB_TEMPORARY);
  //  } // if

getstore_end:
	return result;
 }


STDMETHODIMP O_IXPProvider::TransportLogon(
  LPMAPISUP lpMAPISup,
  ULONG ulUIParam,
  LPTSTR lpszProfileName,
  ULONG *lpulFlags,
  LPMAPIERROR *lppMAPIError,
  LPXPLOGON *lppXPLogon        
)
{
    HRESULT result = S_OK;
//    LPPROFSECT lpProfileObj;
    char *addrA = NULL, *nameA = NULL;
    LPENTRYID eid = NULL;
    HKEY hk = NULL;
    wchar_t szBuf[OTLKCON_REG_VALUE_MAX_LEN+1] = {0};
	DWORD szBufSize = OTLKCON_REG_VALUE_MAX_LEN;
	DWORD readRetVal = 0;
    O_IXPLogonAccount *currAccnt = NULL, *mainAccnt = NULL;
    LPPROFSECT lpProfileObj = NULL;

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
        L"inside IXPPRovider->TransportLogon\n");

    //Check the flags
    if( *lpulFlags & LOGON_NO_CONNECT )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
            L"IXPProvider::TransportLogon() : LOGON_NO_CONNECT set.\n" );
    }
    else if( *lpulFlags & LOGON_NO_DIALOG )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
            L"IXPProvider::TransportLogon() : LOGON_NO_DIALOG set.\n" );
    }
    else if( *lpulFlags & LOGON_NO_INBOUND )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
            L"IXPProvider::TransportLogon() : LOGON_NO_INBOUND set.\n" );
    }
    else if( *lpulFlags & LOGON_NO_OUTBOUND )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
            L"IXPProvider::TransportLogon() : LOGON_NO_OUTBOUND set.\n" );
    }
    else if( *lpulFlags & MAPI_UNICODE )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
            L"IXPProvider::TransportLogon() : MAPI_UNICODE set.\n" );
    }
    else if( *lpulFlags & LOGON_SP_IDLE )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
            L"IXPProvider::TransportLogon() : LOGON_SP_IDLE set.\n" );
    }
    else if( *lpulFlags & LOGON_SP_POLL )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
            L"IXPProvider::TransportLogon() : LOGON_SP_POLL set.\n" );
    }
    else if( *lpulFlags & LOGON_SP_RESOLVE )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
            L"IXPProvider::TransportLogon() : LOGON_SP_RESOLVE set.\n" );
    }

    result = lpMAPISup->AddRef();

    // Create or Load...
	result = GetStore(lpMAPISup, ulUIParam, &lpMsgStore, *lpulFlags);
    if( result != S_OK )
    {
        if( !(*lpulFlags & LOGON_NO_DIALOG) )
        {
            MessageBoxW((HWND)ulUIParam, L"Please configure a Open Connector Data File",
                                    L"No suitable message store found", MB_ICONERROR|MB_OK );
        }

        *lppXPLogon = NULL;
        //return MAPI_E_FAILONEPROVIDER;
        result = MAPI_E_UNCONFIGURED;

        goto logon_end;
    }

    if( xpEnabled == FALSE )
    {
        // We are not allowed to run for now

		*lppXPLogon = NULL;
		return MAPI_E_USER_CANCEL;
    }

    // Create the return object, also save a reference for later
    lpXPLogon = new O_IXPLogon();
    result = lpXPLogon->Create(this, memObj);
    *lppXPLogon = lpXPLogon;

    for( vector<otlkcon_account_details *>::iterator i= accounts->begin();
            i!=accounts->end(); ++i)
    {
        if( (*i)->enabled || (*i)->defaultAccount )
        {
            switch( (*i)->type )
            {
                case OTLKCON_ACCOUNT_TYPE_CALDAV:
                    {
                        O_IXPLogonAccountCalDAV *currCalDAVAccnt 
                            = new O_IXPLogonAccountCalDAV( (*i)->id, 
                                        OTLKCON_ACCOUNT_TYPE_CALDAV, lpXPLogon );

                        currCalDAVAccnt->user = (*i)->user;
                        currCalDAVAccnt->pass = (*i)->pass;
                        currCalDAVAccnt->uri = (*i)->uri;
                        currCalDAVAccnt->name = (*i)->name;
                        currCalDAVAccnt->emailAddr = (*i)->emailAddr;
                        currCalDAVAccnt->enabled = (*i)->enabled;
                        currCalDAVAccnt->defaultAccount = (*i)->defaultAccount;

                        lpXPLogon->accounts.insert( 
                            pair<wstring,O_IXPLogonAccountCalDAV*>( (*i)->id, currCalDAVAccnt ) );
                        currAccnt = currCalDAVAccnt;
                    }
                    break;

                case OTLKCON_ACCOUNT_TYPE_FILE:
                    {
                        O_IXPLogonAccountFile *currFileAccnt 
                            = new O_IXPLogonAccountFile( (*i)->id, 
                                        OTLKCON_ACCOUNT_TYPE_FILE, lpXPLogon );

                        currFileAccnt->user = (*i)->user;
                        currFileAccnt->pass = (*i)->pass;
                        currFileAccnt->uri = (*i)->uri;
                        currFileAccnt->name = (*i)->name;
                        currFileAccnt->emailAddr = (*i)->emailAddr;
                        currFileAccnt->enabled = (*i)->enabled;
                        currFileAccnt->defaultAccount = (*i)->defaultAccount;

                        lpXPLogon->accounts.insert( 
                            pair<wstring,O_IXPLogonAccountFile*>( (*i)->id, currFileAccnt ) );
                        currAccnt = currFileAccnt;
                    }
                    break;

                default:
                    OTLKCON_DEBUGBREAK;
                    break;
            }
            
            if( (*i)->defaultAccount )
            {
                mainAccnt = currAccnt;
            }
        }

    }

    //If we don't have a default account, we have a problem
    if( mainAccnt == NULL )
    {
        result = MAPI_E_UNCONFIGURED;
        goto logon_end;
    }

    // Should we logon?
    if( !(*lpulFlags & LOGON_NO_CONNECT) )
    {
        for( map<wstring,O_IXPLogonAccount*>::iterator i=lpXPLogon->accounts.begin();
            i!=lpXPLogon->accounts.end(); ++i)
        {
            result = i->second->Init();
            if( result != S_OK )
            {
                //Initialization failed
                OTLKCON_DEBUGBREAK;

                i->second->enabled = FALSE;
                if( mainAccnt->id == i->first )
                {
                    // If the default account fails we exit
                    result = MAPI_E_USER_CANCEL;
                    goto logon_end;
                }
                else
                {
                    continue;
                }
            }

            result = i->second->Logon();
            if( result != S_OK )
            {
                //Logon failed
                OTLKCON_DEBUGBREAK;

                if( !(*lpulFlags & LOGON_NO_DIALOG) )
                {
                    MessageBoxW((HWND)ulUIParam, 
                      L"Could not logon to server. Please configure the server \n\
account settings or disable the Transport Provider.", 
                       L"Please check server account settings: Logon failed.",
                       MB_ICONERROR|MB_OK );
                }

                i->second->enabled = FALSE;
                if( mainAccnt->id == i->first )
                {
                    //BUG:  Returning not NULL causes crash because MAPI
                    //      tries to use improperly initialized provider
                    result = MAPI_E_UNCONFIGURED;
                    goto logon_end;
                }
                else
                {
                    continue;
                }
            }

            // TODO: Keep the account's id/entryID pair handy
            accountIds[i->first] = i->second->entryID;
        }

    }

    // Keep the support object around
    lpXPLogon->lpMAPISup = lpMAPISup;

    // Set the status row
    SPropValue tempProps[8] = {0};

    ULONG eidSize = sizeof(O_ENTRYID);

    if( mainAccnt->name.length() == 0 )
        mainAccnt->name = OTLKCON_DEFAULT_ACCOUNT_NAME;
    if( mainAccnt->emailAddr.length() == 0 )
        mainAccnt->emailAddr = OTLKCON_DEFAULT_ACCOUNT_EMAILADDR;

    nameA = (char *)memObj->malloc( (ULONG)mainAccnt->name.length()+1 );
    size_t cConverted = 0;
    errno_t errRes;
    errRes = wcstombs_s(&cConverted, nameA, mainAccnt->name.length()+1,
                        mainAccnt->name.c_str(), mainAccnt->name.length());
    addrA = (char *)memObj->malloc( (ULONG)mainAccnt->emailAddr.length()+1 );
    errRes = wcstombs_s(&cConverted, addrA, mainAccnt->emailAddr.length()+1,
            mainAccnt->emailAddr.c_str(), mainAccnt->emailAddr.length());

	result = lpMAPISup->CreateOneOff( nameA, OTLKCON_ADDRESS_TYPE, 
		addrA, 0, &eidSize, &eid);

    tempProps[0].ulPropTag          = PR_DISPLAY_NAME;
    tempProps[0].Value.lpszA        = OTLKCON_XP_DISPLAY_NAME_A;

    tempProps[1].ulPropTag          = PR_PROVIDER_DISPLAY;
    tempProps[1].Value.lpszA        = OTLKCON_XP_DISPLAY_NAME_A;

    tempProps[2].ulPropTag          = PR_STATUS_CODE;
    tempProps[2].Value.l            = STATUS_AVAILABLE;
	
	if(!(*lpulFlags & LOGON_NO_INBOUND)) 
	{
		tempProps[2].Value.l |= STATUS_INBOUND_ENABLED;
	} // if
	
	if(!(*lpulFlags & LOGON_NO_OUTBOUND))
	{
		tempProps[2].Value.l |= STATUS_OUTBOUND_ENABLED;
	} // if
    
	tempProps[3].ulPropTag = PR_RESOURCE_METHODS;
    tempProps[3].Value.l = STATUS_FLUSH_QUEUES | STATUS_VALIDATE_STATE;
                                       
    tempProps[4].ulPropTag = PR_STATUS_STRING;
    tempProps[4].Value.lpszA = "Available";

    tempProps[5].ulPropTag = PR_IDENTITY_ENTRYID;
    tempProps[5].Value.bin.lpb = (LPBYTE)eid;
    tempProps[5].Value.bin.cb = eidSize;

    tempProps[6].ulPropTag = PR_IDENTITY_DISPLAY;
    tempProps[6].Value.lpszA = nameA;

    tempProps[7].ulPropTag = PR_IDENTITY_SEARCH_KEY;
    tempProps[7].Value.bin.lpb = (LPBYTE)eid;
    tempProps[7].Value.bin.cb = eidSize;

    result = lpMAPISup->ModifyStatusRow( 8, tempProps, 0 );
    if( FAILED(result) )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
            L"ERROR: result == %x", result );
        
        // TODO: cleanup, build error struct 
        result = MAPI_E_USER_CANCEL;
        goto logon_end;
    }
	*lpulFlags = LOGON_SP_IDLE|LOGON_SP_POLL|LOGON_SP_RESOLVE;

    {
        // Notify folders who have been waiting for a XPP
        OTLKCON_ENTERCRITICALSECTION(&otlkcon_xpp_wait_set_cs, "TransportLogon");
        
        for( map< O_ENTRYID *, O_IContainer *, otlkcon_entryid_less>::iterator i
            =otlkcon_xpp_wait_set.begin();
            i!=otlkcon_xpp_wait_set.end(); ++i)
        {
            result = (*i).second->GetXPP();
            if( result != S_OK )
            {
                OTLKCON_DEBUGBREAK;
            }
        }
        otlkcon_xpp_wait_set.clear();
    }

    result = S_OK;

logon_end:
	if(FAILED(result) && lpXPLogon != NULL)
	{
		lpXPLogon->Release();
		lpXPLogon = NULL;
		*lppXPLogon = NULL;
	} // if

    return result;
}

HRESULT O_IXPProvider::ProcessEvent( otlkcon_event_struct *ev )
{
    HRESULT result = S_OK;

    switch( ev->eventType )
    {
        case OTLKCON_EVENT_GET_ID:
            result = OnGetId( ev );
            ;
            break;

        default:
            OTLKCON_DEBUGBREAK;
            break;
    }

    return result;
}

HRESULT O_IXPProvider::OnGetId( otlkcon_event_struct *ev )
{
    HRESULT result = S_OK;
    O_ENTRYID *tmpEid;
    otlkcon_event_struct *event = NULL;

    // Serialize and return the account id/entryId map
    // or just a single id if provided.

    if( ev == NULL )
    {
        // Error
        OTLKCON_DEBUGBREAK;
        return E_FAIL;
    }

    
    if( ev->cbData == 0 )
    {
        // TODO: A list of all IDs is being requested
        OTLKCON_DEBUGBREAK;
    }
    else
    {
        map<wstring,O_ENTRYID*>::iterator i
            = accountIds.find( (wchar_t *)(ev->data) );
        if( i == accountIds.end() )
        {
            // entryID was not found.  Return an empty event.
            event = (otlkcon_event_struct *)memObj->malloc( sizeof otlkcon_event_struct );
            memcpy(&(event->senderEntryID), entryID, sizeof(O_ENTRYID));
            memcpy(&(event->entryID), &(ev->senderEntryID), sizeof(O_ENTRYID));
            event->senderHWND = otlkcon_eventHwnd;

            event->eventType = OTLKCON_EVENT_GET_ID_RES;
            result = otlkcon_event_sendmessage( otlkcon_eventHwnd, otlkcon_eventHwnd, event );
            return S_OK;
        }

        tmpEid = (*i).second;

        event = (otlkcon_event_struct *)memObj->malloc( sizeof(otlkcon_event_struct)
            + sizeof(O_ENTRYID) + tmpEid->dataSize );
        memcpy(&(event->senderEntryID), entryID, sizeof(O_ENTRYID));
        memcpy(&(event->entryID), &(ev->senderEntryID), sizeof(O_ENTRYID));
        memcpy(&(event->data), tmpEid, sizeof(O_ENTRYID)+tmpEid->dataSize);
        event->cbData = sizeof(O_ENTRYID)+tmpEid->dataSize;
        event->senderHWND = otlkcon_eventHwnd;

        event->eventType = OTLKCON_EVENT_GET_ID_RES;
        result = otlkcon_event_sendmessage( otlkcon_eventHwnd, otlkcon_eventHwnd, event );
        return S_OK;
    }

    return result;
}

#endif /* OTLKCON_XP */
